<!--
 * @Description  : 
 * @Version      : 1.0
 * @Author       : seraph
 * @Date         : 2024-12-26 12:19:10
 * @LastEditors  : seraph
 * @LastEditTime : 2025-01-17 11:56:21
-->
<template>
  <h3>获取上一个值</h3>
  <h4 class="red">仅 3.4+ 支持</h4>
  <p>如果需要，可以通过访问计算属性的 getter 的第一个参数来获取计算属性返回的上一个值：</p>
  <pre><code class="javascript" v-html="highlighted1"></code></pre>
  <p>如果你正在使用可写的计算属性的话：</p>
  <pre><code class="javascript" v-html="highlighted2"></code></pre>
  <p>当前 count: {{ alwaysSmall }}，上一次 count：{{ lastCount }}</p>
  <button @click="alwaysSmall = Math.round(Math.random() * 10)">改变 alwaysSmall</button>
  <hr />
  <h3>最佳实践</h3>
  <h4>Getter 不应有副作用</h4>
  <p>
    计算属性的 getter
    应只做计算而没有任何其他的副作用，这一点非常重要，请务必牢记。举例来说，不要改变其他状态、在
    getter 中做异步请求或者更改 DOM！一个计算属性的声明中描述的是如何根据其他值派生一个值。因此
    getter
    的职责应该仅为计算和返回该值。在之后的指引中我们会讨论如何使用侦听器根据其他响应式状态的变更来创建副作用。
  </p>
  <h4>避免直接修改计算属性值</h4>
  <p>
    从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”，每当源状态发生变化时，就会创建一个新的快照。更改快照是没有意义的，因此计算属性的返回值应该被视为只读的，并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。
  </p>
</template>

<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue'
import hljs from 'highlight.js' // 导入 Highlight.js
import javascript from 'highlight.js/lib/languages/javascript' // JavaScript 语言支持
import html from 'highlight.js/lib/languages/xml' // HTML 语言支持
import 'highlight.js/styles/atom-one-light.css' // 导入样式

hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('xml', html)

const rawCode1 = `<script>console.log("Hello, World!");</script&gt;`
const highlighted1 = ref(
  hljs
    .highlight(rawCode1, {
      language: 'javascript',
    })
    .value.replace(/&amp;gt;/g, '&gt;')
    .replace(/&amp;lt;/g, '&lt;'),
)

const rawCode2 = `<script setup>
import { ref, computed } from 'vue'

const count = ref(2)

const alwaysSmall = computed({
  get(previous) {
  // 如果当前值小于等于 3，返回 count 的值，否则返回上一个值
  if (count.value <= 3) {
      return count.value;
    }
    return previous
  },
  set(newValue) {
    count.value=newValue * 2
  }
})
</script&gt;`

const highlighted2 = ref(
  hljs
    .highlight(rawCode2, {
      language: 'javascript',
    })
    .value.replace(/&amp;gt;/g, '&gt;')
    .replace(/&amp;lt;/g, '&lt;'),
)

const count = ref(2)
const lastCount = ref(0)

const alwaysSmall = computed<number>({
  get(previous) {
    console.log(Date.now().toString().slice(-3) + ' 上一个值是：' + previous)
    lastCount.value = previous as number
    return count.value
  },
  set(newValue: number) {
    count.value = newValue * 2
  },
})
</script>

<style scoped>
p {
  font-size: 16px;
  padding: 2px;
  margin: 5px 0;
}

/* 设置预格式化文本的样式 */
pre {
  /* 背景色 */
  background-color: #f1ffed;
  /* 边框 */
  border: 1px solid #ccc;
  /* 缩小内边距 */
  padding: 5px 5px 5px 10px;
  /* 允许滚动 */
  overflow: auto;
  /* 可选：设置上下外边距 */
  margin: 0px 0;
  /* 允许换行 */
  /* normal: 连续的空白字符会被合并为一个空格。文本会在允许的断点处自动换行。
     nowrap：连续的空白字符会被合并为一个空格。文本不会自动换行，即使在允许的断点处也不会换行。
     pre：保留所有的空白字符（包括空格、制表符和换行符）。文本只会在遇到换行符或者 <br> 标签时换行。
     pre-wrap：保留所有的空白字符。文本会在遇到换行符、<br> 标签，或者达到容器边界时换行。
     pre-line：合并连续的空白字符为一个空格，但保留换行符。文本会在遇到换行符、<br> 标签，或者达到容器边界时换行。
   */
  white-space: pre-wrap;
  /* 行高 */
  line-height: 120%;
  /* 定义缩进 */
  tab-size: 4;
}

h3,
h4,
h5,
h6 {
  margin: 25px 0 10px 0;
  line-height: 100%;
}

.bolder {
  font-weight: bolder;
}

.red {
  color: red;
}

button {
  margin: 5px 5px 5px 10px;
  line-height: 150%;
  width: auto;
  padding: 0 10px;
}
</style>
